import random, sys, traceback

from multiprocessing import Process, Lock, JoinableQueue, Event
from datetime import datetime

import lib, sql

from xdh_logging import initLog, LogWriter
from oper import Cleaner, Processor
from subfile import SubFile
from catfile import CatFile
from settings import settings
from cache import cache
from nothing import Nothing
from tpd import TPD

log = initLog("sublist")

class SubWorker(Process):
	Q = JoinableQueue()
	started = Event()

	@classmethod
	def startup(cls, favs = None, proc = None, clean = None):
		if not cls.is_running():
			cls.started.set()
			cls.workers = []
			for i in xrange(settings.data.num_processes):
				worker = Nothing()
				setattr(worker, "running", Event())
				setattr(worker, "process", cls(worker.running, favs, proc, clean))
				cls.workers.append(worker)

	@classmethod
	def is_running(cls):
		if not hasattr(cls, "workers"):
			return False

		running = not (cls.Q.empty() and cls.started.is_set())

		for worker in cls.workers:
			if worker.process.is_alive():
				running |= worker.running.is_set()

		return running

	@classmethod
	def shutdown(cls):
		if not hasattr(cls, "workers"):
			return

		for worker in cls.workers:
			if worker.process.is_alive():
				log.debug("Sending stop to {name} Queue.".format(name = cls.__name__))
				cls.Q.put(lib.STOP)

		cls.Q.join()

		for worker in cls.workers:
			if worker.process.is_alive():
				worker.process.join()

		del cls.workers

		sql.Q.join()

		cls.started.clear()


	def __init__(self, running, favs = None, proc = None, clean = None):
		super(SubWorker, self).__init__()

		self.running = running

		if favs == None:
			self.favs = [False, True]
		else:
			self.favs = favs

		if clean == None:
			self.cleaners = Cleaner.files()
		else:
			Cleaner.set_files(clean)
			self.cleaners = clean

		if proc == None:
			self.processors = Processor.files()
		else:
			Processor.set_files(proc)
			self.processors = proc

		self.start()

	def subProcess(self):
		start = datetime.today()
		log.info("<{cat}> Beginning Processing".format(cat = self.cat))
		
		random.shuffle(self.favs)

		cleaner_start = datetime.today()
		Cleaner.batch(self, self.favs)
		cleaner_diff = datetime.today() - cleaner_start
		self.write_stat("Cleaners Time", cleaner_diff.total_seconds())

		proc_favs = []

		if False in self.favs:
			proc_favs.append(False)

		if True in self.favs:
			proc_favs.append(True)

		processor_start = datetime.today()
		Processor.batch(self, proc_favs)
		processor_diff = datetime.today() - processor_start
		self.write_stat("Processor Time", processor_diff.total_seconds())

		log.info("<{cat}> Processing Complete".format(cat = self.cat))
		diff = datetime.today() - start
		self.write_stat("Total Processing Time", diff.total_seconds())

		totals = {}

		for key, tracks in self.cat_tracks.data.iteritems():
			sub, fav = key[0], key[1]

			if fav:
				sub = "f" + sub

			totals[sub] = len(tracks)

	def initialize(self, cat):
		self.cat = cat
		self.stats = []

		self.weight = lib.catWeight(self.cat)

		self.write_stat("Weight", self.weight)

		start = datetime.today()
		self.tracks = CatFile(self.cat)
		diff = datetime.today() - start
		self.write_stat("Cat Load Time", diff.total_seconds())
		self.write_stat("Total Tracks", len(self.tracks))


	def run(self):
		try:
			while True:
				item = SubWorker.Q.get()
				if item == lib.STOP:
					SubWorker.Q.task_done()
					log.debug("Got stop from {name} Queue.".format(name = SubWorker.__name__))
					break

				self.running.set()

				if not SubWorker.started.is_set():
					SubWorker.started.set()

				self.initialize(item)

				log.info("<{cat}> Starting processing".format(cat = self.cat))
                                
				with SubFile(self.cat) as self.cat_tracks:
					log.info("<{cat}> Loaded Sublist File".format(cat = self.cat))
                                
					self.subProcess()
                                
				self.finish_stats()

				SubWorker.Q.task_done()

				self.running.clear()
                                
				log.info("<{cat}> Finished".format(cat = self.cat))
		except Exception, e:
			log.error("{name}: {msg}".format(name = e.__class__.__name__, msg = e))
			log.error("".join(traceback.format_exception(*sys.exc_info())))

	def finish_stats(self):
		sql.process(sql.CatStatData, "insert", self.stats)

	def write_stat(self, step, value):
		if value > 0:
			data = { 
				'timestamp': datetime.today(),
				'category': self.cat,
				'step': step,
				'value': value,
			}

			self.stats.append(data)


class SubList(object):
	def __init__(self, cats = None, favs = None, clean = None, proc = None):
		if cats == None:
			self.cats = CatFile.getCatList()
		else:
			self.cats = cats

		self.favs = favs
		self.clean = clean
		self.proc = proc

	def __enter__(self):
		return self

	def __exit__(self, type, value, tback):
		SubWorker.shutdown()

		sql.Q.join()

		if type != None:
			log.error("{name}: {msg}".format(name = value.__class__.__name__, msg = value))
			log.error("".join(traceback.format_exception(*sys.exc_info())))

	def start(self):
		SubWorker.started.set()
		SubWorker.startup(self.favs, self.clean, self.proc)

		random.shuffle(self.cats)

		for cat in self.cats:
			SubWorker.Q.put(cat)

	

	def stop(self):
		SubWorker.shutdown()

	@property
	def running(self):
		return SubWorker.is_running()

	@classmethod
	def process(cls, cats = None, favs = None, clean = None, proc = None):
		with SubList(cats, favs, clean, proc) as sublist:
			sublist.start()

			while sublist.running:
				time.sleep(settings.data.sleep)

			sublist.stop()
